//  Copyright 2011 Charlie Garrett
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
//
//  Instruction.cpp
//

#include <sstream>

#include "logic.h"
#include "CodeGenerator.h"
#include "Instruction.h"
#include "Interpreter.h"

namespace logic {
    using namespace std;
    
    string Instruction::ToString() {
        const unsigned LABEL_WIDTH = 8;
        ostringstream os;
        if (!label.first.GetName().empty()) {
            os.width(LABEL_WIDTH);
            const string &prefix = label.first.ToString();
            os << prefix << " [" << label.second << "]: ";
            if (prefix.length() > LABEL_WIDTH) {
                // Insert a newline for long labels.
                os << endl;
                os.width(LABEL_WIDTH + 2);
                os << "";
            }
        } else {
            os.width(LABEL_WIDTH + 2);
            os << "";
        }
        return os.str();
    }
    
    Register Instruction::GetRegister() {
        return Register::Unallocated;
    }
    
    bool PutVariable::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->put_variable(reg, arg_reg);
    }
    
    string PutVariable::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "put_variable " << reg.ToString() << ", " << arg_reg.ToString();
        return os.str();
    }
    
    bool PutValue::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->put_value(reg, arg_reg);
    }
        
    string PutValue::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "put_value " << reg.ToString() << ", " << arg_reg.ToString();
        return os.str();
    }

    bool PutStructure::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->put_structure(&functor, reg);
    }
    
    string PutStructure::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "put_structure " << functor.ToString() << ", " << reg.ToString();
        return os.str();
    }
    
    bool PutList::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->put_list(reg);
    }
    
    string PutList::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "put_list " << reg.ToString();
        return os.str();
    }
    
    bool PutConstant::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->put_constant(&constant, reg);
    }
    
    string PutConstant::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "put_constant " << constant.ToString() << ", " << reg.ToString();
        return os.str();
    }
    
    bool GetVariable::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->get_variable(reg, arg_reg);
    }
    
    string GetVariable::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "get_variable " << reg.ToString() << ", " << arg_reg.ToString();
        return os.str();
    }
    
    bool GetValue::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->get_value(reg, arg_reg);
    }
    
    string GetValue::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "get_value " << reg.ToString() << ", " << arg_reg.ToString();
        return os.str();
    }
    
    bool GetStructure::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->get_structure(&functor, reg);
    }
    
    string GetStructure::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "get_structure " << functor.ToString() << ", " << reg.ToString();
        return os.str();
    }
    
    bool GetList::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->get_list(reg);
    }
    
    string GetList::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "get_list " << reg.ToString();
        return os.str();
    }
    
    bool GetConstant::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->get_constant(&constant, reg);
    }
    
    string GetConstant::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "get_constant " << constant.ToString() << ", " << reg.ToString();
        return os.str();
    }
    
    bool SetVariable::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->set_variable(reg);
    }
    
    string SetVariable::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "set_variable " << reg.ToString();
        return os.str();
    }
    
    bool SetValue::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->set_value(reg);
    }
    
    string SetValue::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "set_value " << reg.ToString();
        return os.str();
    }
    
    bool SetConstant::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->set_constant(&constant);
    }
    
    string SetConstant::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "set_constant " << constant.ToString();
        return os.str();
    }
    
    bool SetVoid::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->set_void(N);
    }
    
    string SetVoid::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "set_void " << N;
        return os.str();
    }
    
    bool UnifyVariable::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->unify_variable(reg);
    }
    
    string UnifyVariable::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "unify_variable " << reg.ToString();
        return os.str();
    }
    
    bool UnifyValue::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->unify_value(reg);
    }
    
    string UnifyValue::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "unify_value " << reg.ToString();
        return os.str();
    }
    
    bool UnifyConstant::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->unify_constant(&constant);
    }
    
    string UnifyConstant::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "unify_constant " << constant.ToString();
        return os.str();
    }
    
    bool UnifyVoid::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->unify_void(N);
    }
    
    string UnifyVoid::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "unify_void " << N;
        return os.str();
    }
    
    bool Call::ResolveLabel(CodeGenerator *codegen) {
        return codegen->LabelOffset(make_pair(functor, 0UL), &code_offset);
    }
    
    bool Call::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->call(code_offset, functor.GetArity());
    }
    
    string Call::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "call " << functor.ToString();
        return os.str();
    }
    
    bool Proceed::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->proceed();
    }
    
    string Proceed::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "proceed";
        return os.str();
    }
    
    bool Halt::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->halt();
    }
    
    string Halt::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "halt";
        return os.str();
    }
    
    bool Allocate::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->allocate(num_permanent_vars);
    }
    
    string Allocate::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "allocate " << num_permanent_vars;
        return os.str();
    }

    bool Deallocate::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->deallocate();
    }
    
    string Deallocate::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "deallocate";
        return os.str();
    }
    
    bool TryMeElse::ResolveLabel(CodeGenerator *codegen) {
        return codegen->LabelOffset(make_pair(functor, 1UL), &next_clause_offset);
    }
    
    bool TryMeElse::Execute(Interpreter *interpreter) {
        return interpreter->try_me_else(next_clause_offset);
    }
    
    string TryMeElse::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "try_me_else " << next_clause_offset;
        return os.str();
    }

    bool RetryMeElse::ResolveLabel(CodeGenerator *codegen) {
        return codegen->LabelOffset(make_pair(functor, clause_num + 1), &next_clause_offset);
    }
    
    bool RetryMeElse::Execute(Interpreter *interpreter) {
        return interpreter->retry_me_else(next_clause_offset);
    }
    
    string RetryMeElse::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "retry_me_else " << next_clause_offset;
        return os.str();
    }

    bool TrustMe::Execute(Interpreter *interpreter) {
        return interpreter->trust_me();
    }
    
    string TrustMe::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "trust_me";
        return os.str();
    }

    bool LinkSourceVariable::Execute(Interpreter *interpreter) {
        return ((Interpreter *)interpreter)->link_source_variable(var, reg);
    }
    
    string LinkSourceVariable::ToString() {
        ostringstream os;
        os << Instruction::ToString() << "link_source_variable " << var->ToString() <<
        ", " << reg.ToString();
        return os.str();
    }

}  // namespace logic
